package tech.yixiyun.framework.kuafu.service;


import tech.yixiyun.framework.kuafu.db.DbKit;
import tech.yixiyun.framework.kuafu.db.sql.Sql;
import tech.yixiyun.framework.kuafu.db.transaction.TransactionEnhancer;
import tech.yixiyun.framework.kuafu.domain.BaseDomain;
import tech.yixiyun.framework.kuafu.enhance.annotation.Enhance;
import tech.yixiyun.framework.kuafu.service.annotation.Service;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

@Service
@Enhance({TransactionEnhancer.class})
public abstract class BaseService<T extends BaseDomain> extends DbKit {

    protected volatile Class<T> domainClass = null;

    public static final String DOMAIN_NOTSET = "未指定DomainClass,请检查继承的BaseService上是否声明泛型Domain类";

    public BaseService() {
        Class superClass = getClass().getSuperclass();
        do {
            Type type = superClass.getGenericSuperclass();
            if (type instanceof ParameterizedType) {
                domainClass = (Class<T>) ((ParameterizedType)type).getActualTypeArguments()[0];
                return;
            } else {
                superClass = superClass.getSuperclass();
            }

        } while (superClass != null);
    }



    /**
     * 代表业务执行成功的ServiceResult，是单例，不要对它进行任何set操作
     */
    public static final Result SUCCESS = new Result(State.SUCCESS);

    /**
     * 代表业务执行后发生错误的ServiceResult，是单例，不要对它进行任何set操作。返回该对象，会触发事务回滚
     */
    public static final Result ERROR = new Result(State.FAIL);

    /**
     * 创建一个状态是SUCCESS的ServiceResult对象，并绑定数据
     * @param data
     * @return
     */
    public Result success(Object data) {
        return new Result(State.SUCCESS, data);
    }
    /**
     * 创建一个状态是SUCCESS的ServiceResult对象，并绑定提示信息和数据
     * @param data
     * @param msg
     * @return
     */
    public Result success(Object data, String msg) {
        return new Result(State.SUCCESS, data, msg);
    }

    /**
     * 创建一个状态是FAIL的ServiceResult对象，并绑定提示信息
     * @param msg
     * @return
     */
    public Result fail(String msg) {
        return new Result(State.FAIL, msg);
    }
    /**
     * 创建一个状态是FAIL的ServiceResult对象，并绑定提示信息和数据
     * @param data
     * @param msg
     * @return
     */
    public Result fail(Object data, String msg) {
        return new Result(State.FAIL, data, msg);
    }


    /**
     * 创建一个状态是WARN的ServiceResult对象，并绑定提示信息
     * @param msg
     * @return
     */
    public Result warn(String msg) {
        return new Result(State.WARN, msg);
    }
    /**
     * 创建一个状态是WARN的ServiceResult对象，并绑定提示信息和数据
     * @param data
     * @param msg
     * @return
     */
    public Result warn(Object data, String msg) {
        return new Result(State.WARN, data, msg);
    }




    public String tableName() {
        if (domainClass == null) {
            throw new NullPointerException(DOMAIN_NOTSET);
        }
        return tableName(domainClass);
    }

    public String tableName(Serializable... args) {
        if (domainClass == null) {
            throw new NullPointerException(DOMAIN_NOTSET);
        }
        return tableName(domainClass, args);
    }



    /**
     * 根据BaseService泛型指定的Domain类构建一个Sql语句
     * @return
     */
    public Sql newSql() {
        if (domainClass == null) {
            throw new NullPointerException(DOMAIN_NOTSET);
        }
        return Sql.build(domainClass);
    }

    /**
     * 根据BaseService泛型指定的Domain类构建一个Sql语句，适用于分表
     * @param args 分表表名参数
     * @return
     */
    public Sql newSql(Serializable... args) {
        if (domainClass == null) {
            throw new NullPointerException(DOMAIN_NOTSET);
        }
        return Sql.build(domainClass, args);
    }


    /**
     * 在泛型声明的Domain类对应的表中，
     * 根据colAndValues拼接成一个 xxx=a and yyy = b and...的语句，然后返回匹配到的第一条数据
     * @param colAndValues 参数必须是2的整数倍，按照column,value,column,value。。。的顺序传入.<br/>column必须为String类型。
     * @param <T>
     * @return
     */
    public <T> T getOne(Serializable[] colAndValues) {
        if (domainClass == null) {
            throw new NullPointerException(DOMAIN_NOTSET);
        }
        return getOne(domainClass, colAndValues);
    }

    /**
     * 在泛型声明的Domain类对应的表中，
     * 根据id从数据库取出数据，并转换成对应类型的对象
     * @param id
     * @param <T>
     * @return
     */
    public <T> T getById(Serializable id) {
        if (domainClass == null) {
            throw new NullPointerException(DOMAIN_NOTSET);
        }
        return getById(domainClass, id);
    }


    /**
     * 在泛型声明的Domain类对应的表中，
     * 根据colAndValues拼接成一个 xxx=a and yyy = b and...的语句，然后返回匹配到的数据集合
     * @param colAndValues 参数必须是2的整数倍，按照column,value,column,value。。。的顺序传入.<br/>column必须为String类型。
     * @param <T>
     * @return
     */
    public <T> List<T> getList(Serializable[] colAndValues) {
        if (domainClass == null) {
            throw new NullPointerException(DOMAIN_NOTSET);
        }
        return getList(domainClass, colAndValues);
    }



    /**
     * 在泛型声明的Domain类对应的表中，
     * 通过sql语句检查是否存在符合条件的记录。
     * @param colAndValues 条件列和值，会自动拼接=。要求参数数量必须是2的整数倍，按照column,value,column,value。。。的顺序传入.<br/>column必须为String类型。
     * @return
     */
    public boolean checkExist(Serializable[] colAndValues) {
        if (domainClass == null) {
            throw new NullPointerException(DOMAIN_NOTSET);
        }
        return checkExist(domainClass, colAndValues);
    }

    /**
     * 在泛型声明的Domain类对应的表中，
     * 通过sql语句检查是否存在符合条件的记录。
     * @param colAndValues 条件列和值，会自动拼接=。要求参数数量必须是2的整数倍，按照column,value,column,value。。。的顺序传入.<br/>column必须为String类型。
     * @param exceptColumn 要排除记录的列名
     * @param exceptValue 要排除记录的值
     * @return
     */
    public boolean checkExistExcept(Serializable[] colAndValues, String exceptColumn, Serializable exceptValue) {
        if (domainClass == null) {
            throw new NullPointerException(DOMAIN_NOTSET);
        }
        return checkExistExcept(getDataSource(domainClass), tableName(domainClass), colAndValues, exceptColumn, exceptValue);
    }

    /**
     * 在泛型声明的Domain类对应的表中，
     * 根据id值更新对应记录的数据。colAndValues 必须按照 列名，值，列名，值...的格式传入
     * @param id
     * @param colAndValues 要更新的列名，值，列名，值...
     * @return
     */
    public int updateById(Serializable id, Serializable[] colAndValues) {
        if (domainClass == null) {
            throw new NullPointerException(DOMAIN_NOTSET);
        }
        return updateById(getDataSource(domainClass), tableName(domainClass), id, colAndValues);
    }

    /**
     * 在泛型声明的Domain类对应的表中，
     * 根据id字段的值进行删除。必须保证表中有id字段
     * @param id
     * @return
     */
    public int delById(Serializable id) {
        if (domainClass == null) {
            throw new NullPointerException(DOMAIN_NOTSET);
        }
        return delById(getDataSource(domainClass), tableName(domainClass), id);
    }

}
